package top.lywivan.netty.reactor.test;


import top.lywivan.netty.reactor.cnxn.NIOServerCnxn;
import top.lywivan.netty.reactor.model.RequestData;
import top.lywivan.netty.reactor.model.ResponseData;
import top.lywivan.netty.reactor.serialize.JdkSerializer;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.security.SecureRandom;
import java.util.Iterator;
import java.util.Set;
import java.util.concurrent.TimeUnit;

public class ReactorClient {

    public void connect(String remoteAddress,int port) {
        try {
            //1、窗口客户端SocketChannel,绑定客户端本地地址(不选默认随机分配一个可用地址)
            SocketChannel socketChannel = SocketChannel.open();
            //2、设置非阻塞模式,
            socketChannel.configureBlocking(false);
            //3、创建Selector
            Selector selector = Selector.open();
            //3、创建Reactor线程
            new Thread(new SingleReactorClient(socketChannel,selector,remoteAddress,port)).start();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private class SingleReactorClient implements Runnable {

        private final SocketChannel socketChannel;
        private final Selector selector;
        private final String remoteAddress;
        private final int port;

        private final ByteBuffer headerBuffer =  ByteBuffer.allocate(4);
        protected ByteBuffer incomingBuffer = headerBuffer;

        private JdkSerializer jdkSerializer;

        public SingleReactorClient(SocketChannel socketChannel, Selector selector, String remoteAddress, int port) {
            this.socketChannel = socketChannel;
            this.selector = selector;
            this.remoteAddress = remoteAddress;
            this.port = port;
            jdkSerializer = new JdkSerializer();
        }

        @Override
        public void run() {
            try {
                //连接服务端
                doConnect(socketChannel,selector);
            } catch (IOException e) {
                e.printStackTrace();
                System.exit(1);
            }
            //5、多路复用器执行多路复用程序
            while (true) {
                try {
                    selector.select(1000);
                    Set<SelectionKey> selectionKeys = selector.selectedKeys();
                    Iterator<SelectionKey> iterator = selectionKeys.iterator();
                    while (iterator.hasNext()) {
                        SelectionKey selectionKey = iterator.next();
                        processKey(selectionKey);
                        iterator.remove();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

        private void processKey(SelectionKey key) throws IOException {
            if (key.isValid()) {
                //6、根据准备就绪的事件类型分别处理
                if (key.isConnectable()) {//服务端可连接事件准备就绪
                    SocketChannel sc = (SocketChannel) key.channel();
                    if (sc.finishConnect()) {
                        key.channel();
                        //6.1、向selector注册可读事件(接收来自服务端的数据)
                        key = sc.register(selector,SelectionKey.OP_READ);
                        //6.2、处理业务 向服务端发送数据
                        doService(sc);
                    }else {
                        //连接失败，退出
                        System.exit(1);
                    }
                }

                if (key.isReadable()) {
                    int i = socketChannel.read(incomingBuffer);
                    if (i < 0) {

                    }

                    // 等到 incomingBuffer 读满
                    if (incomingBuffer.remaining() == 0) {

                        boolean isBody = false;

                        // 如果header 读满
                        if (incomingBuffer == headerBuffer) {
                            incomingBuffer.flip();
                            // 读body的长度
                            isBody = readHeader(key);
                        }else {
                            isBody = true;
                        }

                        // 如果 body 读满
                        if (isBody) {
                            readBody();
                        }else {
                            return;
                        }

                    }
                }
            }
        }

        private void readBody() throws IOException {
            if (incomingBuffer.remaining() !=0) {
                int rc = socketChannel.read(incomingBuffer); // sock is non-blocking, so ok
                if (rc < 0) {

                }
            }

            // 处理 body 数据
            if (incomingBuffer.remaining() ==0) {
                incomingBuffer.flip();
                // deserialize
                byte[] bytes = new byte[incomingBuffer.remaining()];
                incomingBuffer.get(bytes);
                // decode request
                ResponseData response = jdkSerializer.decodeResponse(bytes);
                System.out.println("收到了来自服务端的数据:"+response);

                incomingBuffer = headerBuffer;
            }
        }


        private boolean readHeader(SelectionKey key) throws IOException{
            int len = headerBuffer.getInt();
            if (len < 0 || len > NIOServerCnxn.maxBuffer) {
                throw new IOException("over maxbuffer");
            }
            // 根据 header的值分配要读取的body的大小
            incomingBuffer = ByteBuffer.allocate(len);
            headerBuffer.clear();
            return true;
        }

        private void doConnect(SocketChannel sc, Selector selector) throws IOException {
            System.out.println("客户端成功启动,开始连接服务端");
            //3、连接服务端
            boolean connect = sc.connect(new InetSocketAddress(remoteAddress, port));
            //4、将socketChannel注册到selector并判断是否连接成功，连接成功监听读事件，没有继续监听连接事件
            System.out.println("connect={}"+connect);
            if (connect) {
                sc.register(selector, SelectionKey.OP_READ);
                System.out.println("客户端成功连上服务端,准备发送数据");
                //开始进行业务处理，向服务端发送数据
                doService(sc);
            }else {
                sc.register(selector,SelectionKey.OP_CONNECT);
            }
        }


        private  void doService(SocketChannel socketChannel) throws IOException {
            System.out.println("客户端开始向服务端发送数据");
            //向服务端发送数据
            SecureRandom secureRandom = new SecureRandom();
            RequestData request = new RequestData();
            request.setId(secureRandom.nextLong());
            request.setCode(secureRandom.nextInt());
            request.setComment("hello server " + secureRandom.nextInt(1000));
            System.out.println("发送给服务端的数据为:"+request);
            byte[] bytes = jdkSerializer.encodeRequest(request);

            ByteBuffer writeBuffer = ByteBuffer.allocate(4+bytes.length);
            writeBuffer.putInt(bytes.length);
            writeBuffer.put(bytes);
            writeBuffer.flip();
            socketChannel.write(writeBuffer);

            System.out.println("模拟过一段时间再发送数据");
            try {
                TimeUnit.SECONDS.sleep(secureRandom.nextInt(10));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            RequestData request2 = new RequestData();
            request2.setId(secureRandom.nextLong());
            request2.setCode(secureRandom.nextInt());
            request2.setComment("hello server " + secureRandom.nextInt(1000));
            System.out.println("发送给服务端的数据为:"+request2);
            byte[] bytes2 = jdkSerializer.encodeRequest(request2);

            ByteBuffer writeBuffer2 = ByteBuffer.allocate(4+bytes2.length);
            writeBuffer2.putInt(bytes2.length);
            writeBuffer2.put(bytes2);
            writeBuffer2.flip();
            socketChannel.write(writeBuffer2);
        }

        private String doService(String msg) {
            System.out.println("客户端收到来自服务端响应的数据:{}"+msg);
            return "";
        }
    }
}
